#!/usr/bin/bash
pro_base="$(cd `dirname $(readlink -f $0)`;pwd)"        # program base directory. readlink是为了获取脚本的真实路径
pro_root="${pro_base##*/}"                              # 我的程序的根目录名字.   readlink使得ln创建的软链也能正确地执行
pro_name="$(readlink -f $0 | sed 's#^.*/##; s#.sh$##')" # 删除'/'前的路径,删除'.sh'
log_dir="/var/log/${pro_root}/${pro_name}"
stdout_log="${log_dir}/${pro_name}.log"
stderr_log="${log_dir}/stderr.log"
export pro_base pro_name stdout_log                # 传给 smon_main.sh 中的 Source 函数

Source(){
    source ${pro_base}/shared/include.sh || exit 1
    Include ${pro_base}/${pro_name}/usage.sh
    Include ${pro_base}/shared/prompt.sh
}
export -f Source                         # 传给 Start_smon 中的 smon_main.sh

Check_condition(){
    Help "$@"
    [[ $# -ne 1 ]] && Usage && exit 1
    [[ -d "${log_dir}" ]] || Eval_exit "mkdir -p '${log_dir}'"
}

Pro_info(){
    local pid="$(ps -ef |grep "bash ${pro_base}/${pro_name}/${pro_name}_main.sh"|grep -v grep |awk '{print $2}')"
    if [[ $pid != "" ]]; then
        PWarn "Smon进程已启动,进程信息如下:"
        ps -ef | grep -E "CMD|bash ${pro_base}/${pro_name}/${pro_name}_main.sh"
        exit 1
    fi
}

Start_smon(){
    PInfo "Smon进程即将启动"
    bash ${pro_base}/${pro_name}/${pro_name}_main.sh >>${stdout_log} 2>>${stderr_log} &
}                           # 写在脚本中的nohup命令不产生"nohup: ignoring input and appending output to ‘nohup.out’"的stderr

Kill_smon(){
    local pid="$(ps -ef |grep "bash ${pro_base}/${pro_name}/${pro_name}_main.sh"|grep -v grep |awk '{print $2}')"
    while [[ $pid != "" ]]
    do
        kill $pid
        sleep 0.2
        pid="$(ps -ef |grep "bash ${pro_base}/${pro_name}/${pro_name}_main.sh"|grep -v grep |awk '{print $2}')"
    done
    PInfo "Smon进程已关闭"
}

Reload_smon(){
    local pid="$(ps -ef O+T|grep "bash ${pro_base}/${pro_name}/${pro_name}_main.sh"|head -n1 |awk '{print $2}')"
    if [[ $pid != "" ]]; then
        kill -1 $pid
        PInfo "准备 reload 配置文件,已向Smon进程发SIGHUP信号,pid是:" $pid
    else
        PNote "Smon进程不存在"
    fi
}

Install_dependence(){
    Eval_cont "type sar" || Eval_exit "yum install -y sysstat"
    Eval_cont "type iftop" || Eval_exit "yum install -y iftop"
    Eval_cont "type jstack" || PWarn "缺少 jstack 命令,不能很好地监控Java程序的线程信息"
    Eval_cont "type jstat" || PWarn "缺少 jstat 命令,不能很好地监控Java程序的内存使用信息"
    Eval_cont "type jmap" || PWarn "缺少 jmap 命令,不能抓取Java程序的heapdump信息"
    [[ -f /etc/logrotate.d/healthCheck ]] && rm -f /etc/logrotate.d/healthCheck # 以前 smon 叫 healthCheck
    [[ -f /etc/logrotate.d/smon ]] || cat <<EOF >/etc/logrotate.d/smon          # EOF不带引号时会作'参数替换',若写作cat<<'EOF',则不作'参数替换'
# 每天 rotate 日志
daily
# 就算日志不存在,也不报错,继续其他 rotate 工作
missingok
# 若日志为空,就不 rotate
notifempty
copytruncate
# rotate 后的日志进行压缩
compress
# delaycompress: 最近rotate的那个日志不要压缩,必须与compress一起使用
${log_dir}/*.log {
    rotate 5
}
EOF
    Eval_cont "ls -l /etc/logrotate.d"
    # zabbix agent 以 myzabbix 身份检查 SWAP, smon 以 root 身份检查 SWAP
    if ! id myzabbix; then
#        [[ `grep -Eac pam_pwquality /etc/pam.d/system-auth` -gt 0 ]] && sed -i '/pam_pwquality/s/.*pam_pwquality.*/#&/' /etc/pam.d/system-auth  # 行首添加‘#’注释
#        [[ `grep -Eac 'pam_unix.so md5' /etc/pam.d/system-auth` -gt 0 ]] && sed -i '/pam_unix.so md5/s/.*pam_unix\.so md5.*/#&/' /etc/pam.d/system-auth  # 行首添加‘#’注释,但有些系统不是md5,而是sha512
#        sed -i '/pam_unix.so md5/a password\tsufficient\tpam_unix.so sha512' /etc/pam.d/system-auth
#        [[ `grep -Eac 'pam_unix.so sha' /etc/pam.d/system-auth` -gt 0 ]] && sed -i '/pam_unix.so sha/s/.*pam_unix\.so sha.*/#&/' /etc/pam.d/system-auth
#        sed -i '/pam_unix.so sha/a password\tsufficient\tpam_unix.so sha512' /etc/pam.d/system-auth
        # 若创建用户失败了,执行上面三行命令后重新创建用户
        Eval_exit "groupadd -g1004 myzabbix && useradd -u1004 -g1004 myzabbix && echo 'myzabbix' |passwd --stdin myzabbix"
    fi
    ! ls /root/.toprc && Eval_exit "cd /root" && Eval_exit "wget 'https://pbkb.houseschan.cn/.toprc'"  # .toprc目前仅适用于CentOS7
    ! ls /root/.toprc && PWarn "[.toprc 文件不存在]: 无法监控 SWAP 的使用率." && PInfo "可以按下面5个步骤配置top命令:
1.执行top,按'l'不显示第1行的Load Avg/Uptime,按3次't'不显示Task/Cpu states,按3次'm'不显示Mem/Swap usage
2.按'f',依照屏幕的快捷键提示,取消显示PR/NI/VIRT/RES/SHR/S/TIME+
3.依照屏幕的快捷键提示,显示SWAP,并把SWAP移动到COMMAND的前面(让SWAP出现在第5列),在SWAP处按下's'(按swap排序)
4.按'q'返回,再按下'x','b'(高亮显示)
5.按'W'保存配置到/root/.toprc,按'q'退出top,再执行: top -b -n1|head"
}

case $1 in
    -b)
        {
        Source
        Check_condition "$@"
        Pro_info
        Eval_cont "Start_smon"
        } &>> ${stdout_log}
    ;;
    -f)
        Source
        Check_condition "$@"
        Pro_info
        touch ${stdout_log}                     # 避免下面的 tail 先于 Start_smon 执行时,若不存在${stdout_log}会出错
        Eval_cont "Start_smon"
        Eval_cont "tail -f ${stdout_log}"
    ;;
    -i)
        Source                                  # 配置logrotate日志
        Check_condition "$@"
        Install_dependence
    ;;
    -k)
        Source
        Check_condition "$@"
        Kill_smon
    ;;
    -r)
        Source
        Check_condition "$@"
        Kill_smon
        touch ${stdout_log}
        Eval_cont "Start_smon"
        Eval_cont "tail -f ${stdout_log}"
    ;;
    -l)
        Source
        Check_condition "$@"
        Reload_smon
        Eval_cont "tail -f ${stdout_log}"
    ;;
    *)
        Source
        Usage && exit 1
    ;;
esac
